/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/** 
 * \file 
 * \brief Simple module to implement security features - implementation.
 * \author Antti Maula <antti.maula@tkk.fi>
 */
#include "apsecurity.h"
#include "ownutils.h"
#include "owndebug.h"
#include <inttypes.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>


#ifndef _DISABLE_OPENSSL_
# include <openssl/hmac.h>
# include <openssl/sha.h>
#else
# include "crc16.h"
#endif


//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
#define GIMNET_HMACSIZE_BYTES (160 / 8)

void apsecurityCalculateHMAC(uint8_t *hmac,
                             unsigned int *hmacsize,
                             const uint8_t *key,
                             const unsigned int keylen,
                             const uint8_t *data,
                             const unsigned int datalen)
{
#ifndef _DISABLE_OPENSSL_
  HMAC_CTX context;
  HMAC_CTX_init(&context);
  HMAC_Init_ex(&context, key, keylen, EVP_sha1(), NULL);
  HMAC_Update(&context, data, datalen);
  HMAC_Final(&context, hmac, hmacsize);
  HMAC_CTX_cleanup(&context);
  assert(*hmacsize == GIMNET_HMACSIZE_BYTES);
#else
# warning OpenSSL Disabled! Using completely insecure signing!
  int i = 0;
  assert(*hmacsize >= GIMNET_HMACSIZE_BYTES);

  unsigned int processed_data_size = 0;
  const unsigned int chunk_size = datalen / 10;
  const uint32_t crc16_key = crc16((const char *)key, keylen);
  uint16_t *hmacptr = (uint16_t *)hmac;
  const uint8_t  *dataptr = data;

  // We need 20 bytes, which is 10 different crc16 values.
  for(i = 0; i < GIMNET_HMACSIZE_BYTES; i += 2) {
    const unsigned int chunk = MIN(chunk_size, datalen - processed_data_size); 
    const uint32_t crc16_data = crc16((const char *)dataptr, chunk);
    *hmacptr = crc16_data ^ crc16_key;

    hmacptr++;
    processed_data_size += chunk;
    dataptr += chunk;
  }
  
#endif
}
//*****************************************************************************

int apsecurityReadSecretKeyFromFile(uint8_t *key, 
                                    const unsigned int bytes, 
                                    const char *filename)
{
  int result = -1;
  
  FILE *f = fopen(filename, "rb");
  if (f) {
    struct stat s;
    if (!fstat(fileno(f), &s)) {
      // Now we know the file size.
      const off_t to_read = MIN(bytes, s.st_size);
      
      size_t r = fread(key, to_read, 1, f);
      if (r == 1) {
        result = (int)to_read;
      }
    }
  }

  return result;
}
//*****************************************************************************

int apsecurityReadAuthenticationFile(TAPSecurityAuthentication *auth,
                                     const char *filename)
{
  char line[500000]; // Big enough
  int result = 0;

  // Clear.
  memset(auth, 0x00, sizeof(TAPSecurityAuthentication));
  
  FILE *f = fopen(filename, "rb");
  if (f) {
    while(!feof(f)) {
      char *s = fgets(line, sizeof(line), f);
      if (s) {
        const int llen = strlen(s);
        if (llen > 0 && s[llen-1] == '\n') 
          s[llen-1] = '\0'; 
        
        if (isgraph(s[0])) {
          if (s[0] == '#') {
            //dPrint(ODTEST,"Comment char. Skip.");
          } else {
            char *sdiv = index(s, ':');
            if (sdiv) {
              *sdiv = '\0';
              ++sdiv;
              //dPrint(ODTEST,"Key: %s, Data: %s", s, div);
                    
              if (strcmp(s, "APClient") == 0) {
                strcpy((char *)auth->secretAPClient, sdiv);
                auth->secretAPClientSize = strlen(sdiv);

              } else if (strcmp(s, "APService") == 0) { 
                strcpy((char *)auth->secretAPService, sdiv);
                auth->secretAPServiceSize = strlen(sdiv);

              } else if (strcmp(s, "AP2AP") == 0) {
                strcpy((char *)auth->secretAP2AP, sdiv);
                auth->secretAP2APSize = strlen(sdiv);

              } else {
                dPrint(ODWARN,"Unknown GIMnetAuth key '%s', ignore", s);

              }
            }
            
          }
          
        } else {
          //dPrint(ODTEST,"Not a graph character");

        } 
      } else {
        //dPrint(ODTEST,"EOF.");

      }
      
    }
  } else {
    dPrint(ODTEST,"Failed to open file %s", filename);
  
  }
  
  result = (auth->secretAPClientSize && auth->secretAPServiceSize && auth->secretAP2APSize) ? 0 : -1;

  return result;
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
#ifdef UNIT_TEST
// gcc -D LINUX_OS -D UNIT_TEST apsecurity.c -I ../../utils/ ../../utils/ownutils.c ../../utils/owndebug.c ../../utils/crc16.c -lrt -o apsecurity_unittest -lssl

int main(void)
{
  debugInit();
  debugSetGlobalDebugLvl(2);
  const char keys[10][100] = { "foobar", 
                               "asdqwe",
                               "132123d",
                               "asdasdasd",
                               "sdfdsfxvc",
                               "asdasdasd",
                               "qw1243dsf",
                               "asd",
                               "a",
                               "fsdfxcvxvcdasfsdfdsf" };
  const char datas[10][100] = { "qwertyuiopasdfghjklzxcvbnm",
                                "asdasdkljzxclkjxcvkjhklafasdf8967896dasf7kjxhvckjhzxcv",
                                "asdkjlhzxck",
                                "asd",
                                "dlkjzx6zx86czxcbvzxjchg76z5c7gasdjhgaksgkjxgckjzgxckjgz<xcjhgz<xcjhgas875as76d5asdjhzxgcjhzgxcva",
                                "asdjhagsd8768z76xczuhxgchgzxchgf",
                                "asdasdkjh876zx8c76z8x76c8z7x6c87zx6cjhgdas",
                                "czkjlxhckjhsxdKJHKJHK(/&i8768a76sd87%&%&%&(/%(/&asd",
                                "asdlkjzxcjbzxc,m,zxc,.,.xc.-z-xc.zxlckhasd8976",
                                "           987asd897asd987asdjhgzxckjhzxckjhzxckjHKJH" };
  
  uint8_t hmac[20];
  int hmac_size = 20;

  printf("Just calculate:\n");

  int d;
  for (d=0; d < 10; ++d) {
    apsecurityCalculateHMAC(hmac, &hmac_size, 
                            keys[d], strlen(keys[d]), 
                            datas[d], strlen(datas[d]));
    assert(hmac_size == 20);
    
    printf("Key: '%s', data: '%s':\n\t", keys[d], datas[d]);
    int i;
    for(i=0; i < hmac_size; ++i) {
      printf("%02x", hmac[i]);
    }
    printf("\n\n");    
  }

  printf("Test file read\n");
  TAPSecurityAuthentication aa;
  apsecurityReadAuthenticationFile(&aa, "test.cred");
  printf("Got APClient key: '%s'\n", aa.secretAPClient);
  printf("Got APService key: '%s'\n", aa.secretAPService);
  printf("Got AP2AP key: '%s'\n", aa.secretAP2AP);
  
}
#endif
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
